using System;
using System.Text;
using System.Collections;
using System.Text.RegularExpressions ;

namespace NCover
{
	using Objects;

	/// <summary>
	/// This class should not import System.IO.
	/// </summary>
	public class VbInstrumenter
	{
		ArrayList _coveragePoints = new ArrayList();
		readonly int _baseCoveragePoint;
		string _uninstrumentedSrc;
		readonly string _filename;

		//VBied
		readonly static Regex statementRegex = new Regex(@"(\s*)((Throw)\s)", RegexOptions.Compiled & RegexOptions.Multiline);
		//readonly static Regex statementRegex = new Regex(@"(\s*)((For Each|Throw|Return)\s)", RegexOptions.Compiled);

		//Vbied:
		readonly static Regex namespaceRegex = new Regex(@"Namespace\s+([^\s]+)\s*", RegexOptions.Compiled & RegexOptions.Multiline);

		//VBied
		readonly static Regex ifRegex = new Regex(@"(\s+(If|ElseIf)\s)(.*?)(Then)", RegexOptions.Compiled & RegexOptions.Multiline);

		//VBied
		readonly static Regex iifRegex = new Regex(@"(\s*IIf\(.*?),", RegexOptions.Compiled & RegexOptions.Multiline);

		//not after select case or end case....
		readonly static Regex selectCaseRegex = new Regex(@"([^dt]\sCase\s)(.*)", RegexOptions.Compiled & RegexOptions.Multiline);


		//^d is for End...
		readonly static Regex whileRegex = new Regex(@"[^d]\sWhile\s.*", RegexOptions.Compiled & RegexOptions.Multiline);

		//not sure how to do for int i = 1 to 10
		//readonly static Regex conditionalsRegex = new Regex(@"(\s*(If|while|for)\s*\()(.*?\))", RegexOptions.Compiled);

		//not sure how to catch catches....
		//readonly static Regex catchBlockRegex = new Regex(@"\s*catch\s*\(.*?\)\s*{", RegexOptions.Compiled);

		
		public VbInstrumenter(int baseCoveragePoint, string src, string filename)
		{
			_uninstrumentedSrc = src;
			_filename = filename;
			_baseCoveragePoint = baseCoveragePoint;
		}

		private IList _comments; 
		private string _namespace;

		//Old regex:
		//readonly static Regex inserterRegex = new Regex(@"([{;}]\s*)((if|for|foreach)\s*\()", RegexOptions.Compiled);

		public CoveragePoint[] Do(out string instrumentedSrc, IList comments)
		{
			_comments = comments;

			/*Uncoment this code to display which areas of code we do not instrument as we consider them to be comments.
						StringBuilder sb = new StringBuilder();
						for (int i = 0; i < _uninstrumentedSrc.Length; i++)
						{
							sb.Append(isInComment(i)? Char.ToUpper(_uninstrumentedSrc[i]) : Char.ToLower(_uninstrumentedSrc[i]));
						}
						Console.WriteLine(sb.ToString());
			*/

			Match match = namespaceRegex.Match(_uninstrumentedSrc);
			if (match.Success)
			{
				_namespace = match.Groups[1].Value;
			}
			else
			{
				_namespace = Report.CoverageNamespace.DEFAULT_NAMESPACE;
			}

			//ifs must come before statements otherwise we instrument our always true ifs.
			_uninstrumentedSrc = ifRegex.Replace(_uninstrumentedSrc, new MatchEvaluator(instrumentIfs));

			_uninstrumentedSrc = statementRegex.Replace(_uninstrumentedSrc, new MatchEvaluator(instrumentStatements));
			
			_uninstrumentedSrc = iifRegex.Replace(_uninstrumentedSrc, new MatchEvaluator(instrumentIIfs));

			_uninstrumentedSrc = selectCaseRegex.Replace(_uninstrumentedSrc, new MatchEvaluator(instrumentSelectCase));

			instrumentedSrc = whileRegex.Replace(_uninstrumentedSrc, new MatchEvaluator(instrumentWhiles));
			//instrumentedSrc = catchBlockRegex.Replace(_uninstrumentedSrc, new MatchEvaluator(instrumentCatchBlocks));
			
			NCover.NCoverCheck.Add(236); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(236); NCover.NCoverCheck.Add(235); NCover.NCoverCheck.Add(235); NCover.NCoverCheck.Add(234); NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(236); NCover.NCoverCheck.Add(236); NCover.NCoverCheck.Add(236); NCover.NCoverCheck.Add(236); NCover.NCoverCheck.Add(236); NCover.NCoverCheck.Add(236); NCover.NCoverCheck.Add(236); return (CoveragePoint[]) _coveragePoints.ToArray(typeof(CoveragePoint));
		}

		public bool isInComment(int position)
		{
			if (_comments != null)
				foreach (Insert insert in _comments)
				{
					if (insert.InsertPoint <= position && position < (insert.InsertPoint + insert.Length))
						return true;

					//if list is sorted by index we can break once InsertPoint > position
				}

			NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(236); NCover.NCoverCheck.Add(236); NCover.NCoverCheck.Add(235); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(237); return false;
		}

		/// <summary>
		/// Delegate Called by matcher.
		/// Line numbers to be retrofitted later.
		/// </summary>
		private string instrumentStatements(Match match)
		{
			if (!match.Success)
				return match.Value;

			//			Console.WriteLine(point.ToString());
			//how many chars in is the if?

			string pre = match.Groups[1].Value;
			string post = match.Groups[2].Value;

			//int ifIndex = 0;//match.Value.IndexOf("if");
			//int foreachIndex = match.Value.IndexOf("foreach");
			

			int absoluteInsertPoint = match.Index + pre.Length;
			//string before = _uninstrumentedSrc.Substring(0, absoluteInsertPoint);
			//string after = _uninstrumentedSrc.Substring(absoluteInsertPoint);

			if (isInComment(absoluteInsertPoint))
				return match.Value;

			CoveragePoint point = new CoveragePoint(_filename, _namespace,_coveragePoints.Count + 1, _baseCoveragePoint);
			_coveragePoints.Add(point);

			point.LineNumber = IndexToLineNumber(absoluteInsertPoint);
			
			NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(236); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(238); return pre + "If " +  point.InsertCoverageCode() + " Then " +  post;
		}

		/// <summary>
		/// Delegate Called by matcher.
		/// </summary>
		private string instrumentIfs(Match match)
		{
			if (!match.Success)
				return match.Value;

			string theIf = match.Groups[1].Value;
			string condition = match.Groups[3].Value;
			string theThen = match.Groups[4].Value;
	
			CoveragePoint point = new CoveragePoint(_filename, _namespace,_coveragePoints.Count + 1, _baseCoveragePoint);
			_coveragePoints.Add(point);

			point.LineNumber = IndexToLineNumber(match.Index + theIf.Length );
			
			NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(237); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(239); return theIf
					+ condition + " AndAlso "
					+ point.InsertCoverageCode()
					+ theThen;
		}
			
		/// <summary>
		/// Delegate Called by matcher.
		/// </summary>
		private string instrumentIIfs(Match match)
		{
			if (!match.Success)
				return match.Value;

			string theIf = match.Groups[1].Value;
	
			CoveragePoint point = new CoveragePoint(_filename, _namespace,_coveragePoints.Count + 1, _baseCoveragePoint);
			_coveragePoints.Add(point);
			
			point.LineNumber = IndexToLineNumber(match.Index + theIf.Length );
			
			NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(238); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(240); return theIf
				+ " AndAlso "
				+ point.InsertCoverageCode()
				+ ",";
		}
		

		/// <summary>
		/// Delegate Called by matcher.
		/// </summary>
		private string instrumentWhiles(Match match)
		{
			if (!match.Success)
				return match.Value;

			CoveragePoint point = new CoveragePoint(_filename, _namespace,_coveragePoints.Count + 1, _baseCoveragePoint);
			_coveragePoints.Add(point);

			point.LineNumber = IndexToLineNumber(match.Index);

			string code, comment;
			SplitComment(match.Value, out code, out comment);
		
			NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(243); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(239); NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(241); return code + " AndAlso " + point.InsertCoverageCode() + comment;
		}

		/// <summary>
		/// Delegate Called by matcher.
		/// </summary>
		private string instrumentSelectCase(Match match)
		{
			if (!match.Success)
				return match.Value;

			//can't add into case else
			if (match.Groups[2].Value.StartsWith("Else"))
				return match.Value;

			CoveragePoint point = new CoveragePoint(_filename, _namespace,_coveragePoints.Count + 1, _baseCoveragePoint);
			_coveragePoints.Add(point);

			point.LineNumber = IndexToLineNumber(match.Index);

			string code, comment;
			SplitComment(match.Groups[2].Value , out code, out comment);
		
			NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(244); NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(240); NCover.NCoverCheck.Add(243); NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(242); return match.Groups[1] + code + " AndAlso " + point.InsertCoverageCode() + comment;
		}

		/// <summary>
		/// also removes trailing /r
		/// </summary>
		private void SplitComment(string input, out string code, out string comment)
		{
			int commerPositon = input.IndexOf("'") ;
			if (commerPositon > 0)
			{
				code = input.Substring (0, commerPositon);
				comment = input.Substring(commerPositon);
			}
			else
			{
				code = input ;
				comment = "";

				if (code[code.Length-1] == '\r')
				{
					code = code.Substring(0, code.Length-1);
					comment = "\r";
				}
			}
		}

		private int IndexToLineNumber(int index)
		{
			int lineNumber = 1;
			for(int i = 0; i <= index; i++)
			{
				if (_uninstrumentedSrc[i] == '\n')
					lineNumber++;
			}

			NCover.NCoverCheck.Add(243); NCover.NCoverCheck.Add(245); NCover.NCoverCheck.Add(243); NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(242); NCover.NCoverCheck.Add(241); NCover.NCoverCheck.Add(244); NCover.NCoverCheck.Add(243); NCover.NCoverCheck.Add(243); NCover.NCoverCheck.Add(243); NCover.NCoverCheck.Add(243); NCover.NCoverCheck.Add(243); NCover.NCoverCheck.Add(243); NCover.NCoverCheck.Add(243); return lineNumber;
		}
	}
}













